#include "Rendering.h"
#include <string>
Rendering::Rendering()
{

}
Rendering::~Rendering()
{

}
void Rendering::Initialize(int _screenWidth, int _screenHeight, HWND hwnd,bool _verticalSynchronizeEnabled,bool using4XMSAA,bool _fullScreen, float _screenDepth, float _screenNear,Camera* _camera)
{
	directXClass = new DirectXClass;
	directXClass->Initialize(_screenHeight, _screenWidth, _verticalSynchronizeEnabled, using4XMSAA, _fullScreen, hwnd, _screenDepth, _screenNear);
	camera = _camera;
	m_lightAndMaterialShader = new LightAndMaterialShader;
	m_lightAndMaterialShader->Initialize(directXClass->GetDevice());
	m_textureShader = new TextureShader;
	m_textureShader->Initialize(directXClass->GetDevice());

	D3D11_SAMPLER_DESC samplerStateDescription;
	samplerStateDescription.Filter =D3D11_FILTER_ANISOTROPIC;
	samplerStateDescription.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerStateDescription.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerStateDescription.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerStateDescription.MipLODBias = 0.0f;
	samplerStateDescription.MaxAnisotropy = 16;
	samplerStateDescription.ComparisonFunc = D3D11_COMPARISON_NEVER;
	samplerStateDescription.MinLOD = -D3D11_FLOAT32_MAX;
	samplerStateDescription.MaxLOD=D3D11_FLOAT32_MAX;

	HRESULT result=directXClass->GetDevice()->CreateSamplerState(&samplerStateDescription, &m_samplerState);
}
void Rendering::Shutdown()
{
	if (m_lightAndMaterialShader)
	{
		m_lightAndMaterialShader->Shutdown();
		delete m_lightAndMaterialShader;
		m_lightAndMaterialShader = 0;
	}
	if (directXClass)
	{
		directXClass->Shutdown();
		delete directXClass;
		directXClass = 0;
	}
}
void Rendering::Update(std::vector<GameObject*>gameObjects, std::vector<DirectionalLight*>directionalLights, std::vector<PointLight*>pointLights, std::vector<SpotLight*>spotLights)
{
	DirectX::XMMATRIX viewMatrix, projectionMatrix, worldMatrix;
	directXClass->BeginScene(0.0f, 0.0f, 0.5f, 1.0f);
	viewMatrix = camera->Render();
	projectionMatrix = directXClass->GetProjectionMatrix();
	//int tempIndexCount=renderingModels->Render(directXClass->GetDevice(), directXClass->GetDeviceContext());
	//shader->Render(directXClass->GetDeviceContext(), tempIndexCount, &worldMatrix, &viewMatrix, &projectionMatrix);

	directXClass->GetDeviceContext()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

	DirectX::XMFLOAT3 cameraPosition=camera->m_transform->GetPosition();
	int directionalLightsNumber = directionalLights.size();
	int pointLightsNumber = pointLights.size();
	int spotLightsNumber = spotLights.size();
	D3D11_MAPPED_SUBRESOURCE mappedResource;

	unsigned int stride = sizeof(DirectXClass::DirectXVertexType);
	unsigned int offset = 0;
	for (int i = 0;i < gameObjects.size();++i)
	{
		if (gameObjects[i]->GetIsVisible())
		{
			//Update rendering buffer if needed
			{
				if (gameObjects[i]->GetNeedToUpdateRenderingBuffer())
				{
					directXClass->CreateRenderingBuffer(gameObjects[i]);
				}
				gameObjects[i]->SetNeedToUpdateRenderingBuffer(false);
			}

			//Update texture if needed
			{
				if (gameObjects[i]->GetNeedToUpdateTexture())
				{
					directXClass->CreateTexture(gameObjects[i]);
				}
				gameObjects[i]->SetNeedToUpdateTexture(false);
			}

			//render
			{
				int shaderType = gameObjects[i]->GetShaderType();

				if (shaderType == 1)//LightAndMaterial
				{
					directXClass->GetDeviceContext()->IASetInputLayout(m_lightAndMaterialShader->layout);
					directXClass->GetDeviceContext()->VSSetShader(m_lightAndMaterialShader->vertexShader, NULL, 0);
					directXClass->GetDeviceContext()->PSSetShader(m_lightAndMaterialShader->pixelShader, NULL, 0);

					DirectX::XMMATRIX worldMatrix = gameObjects[i]->m_transform->GetWorldMatrix();

					//worldInverseTransposeMatrix is used to transform normal vector.
					DirectX::XMMATRIX worldInverseTransposeMatrix = worldMatrix;
					worldInverseTransposeMatrix.r[3] = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
					DirectX::XMVECTOR worldInverseTransposeMatrixDet = DirectX::XMMatrixDeterminant(worldInverseTransposeMatrix);
					worldInverseTransposeMatrix = DirectX::XMMatrixTranspose(DirectX::XMMatrixInverse(&worldInverseTransposeMatrixDet, worldInverseTransposeMatrix));

					directXClass->GetDeviceContext()->IASetVertexBuffers(0, 1, gameObjects[i]->GetVertexBuffer(), &stride, &offset);
					directXClass->GetDeviceContext()->IASetIndexBuffer(*gameObjects[i]->GetIndexBuffer(), DXGI_FORMAT_R32_UINT, 0);

					directXClass->GetDeviceContext()->Map(m_lightAndMaterialShader->m_vertexShaderBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
					LightAndMaterialShader::VertexShaderBufferType* vertexShaderBufferPtr = (LightAndMaterialShader::VertexShaderBufferType*)mappedResource.pData;
					vertexShaderBufferPtr->world = worldMatrix;
					vertexShaderBufferPtr->worldInverseTranspose = worldInverseTransposeMatrix;
					vertexShaderBufferPtr->view = viewMatrix;
					vertexShaderBufferPtr->projection = projectionMatrix;
					directXClass->GetDeviceContext()->Unmap(m_lightAndMaterialShader->m_vertexShaderBuffer, 0);
					directXClass->GetDeviceContext()->VSSetConstantBuffers(0, 1, &(m_lightAndMaterialShader->m_vertexShaderBuffer));

					directXClass->GetDeviceContext()->Map(m_lightAndMaterialShader->m_pixelShaderBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
					LightAndMaterialShader::PixelShaderBufferType* pixelShaderBufferPtr = (LightAndMaterialShader::PixelShaderBufferType*)mappedResource.pData;
					pixelShaderBufferPtr->material = gameObjects[i]->GetMaterial()->materialStructure;
					pixelShaderBufferPtr->eyePositionWorld = DirectX::XMFLOAT4(cameraPosition.x, cameraPosition.y, cameraPosition.z, 1.0f);
					pixelShaderBufferPtr->lightsNumber = DirectX::XMINT4(directionalLightsNumber, pointLightsNumber, spotLightsNumber, 0);
					for (int i = 0; (i < directionalLightsNumber) && (i < 100); ++i)
					{
						pixelShaderBufferPtr->directionalLights[i] = directionalLights[i]->GetLightStructure();
					}
					for (int i = 0; (i < pointLightsNumber) && (i < 100); ++i)
					{
						pixelShaderBufferPtr->pointLights[i] = pointLights[i]->GetLightStructure();
					}
					for (int i = 0; (i < spotLightsNumber) && (i < 100); ++i)
					{
						pixelShaderBufferPtr->spotLights[i] = spotLights[i]->GetLightStructure();
					}
					directXClass->GetDeviceContext()->Unmap(m_lightAndMaterialShader->m_pixelShaderBuffer, 0);
					directXClass->GetDeviceContext()->PSSetConstantBuffers(0, 1, &(m_lightAndMaterialShader->m_pixelShaderBuffer));

					directXClass->GetDeviceContext()->DrawIndexed(gameObjects[i]->GetIndexNumber(), 0, 0);
				}
				else if (shaderType == 2)//Texture
				{
					directXClass->GetDeviceContext()->IASetInputLayout(m_textureShader->m_layout);
					directXClass->GetDeviceContext()->VSSetShader(m_textureShader->m_vertexShader, NULL, 0);
					directXClass->GetDeviceContext()->PSSetShader(m_textureShader->m_pixelShader,NULL,0);
					directXClass->GetDeviceContext()->PSSetSamplers(0, 1, &m_samplerState);

					DirectX::XMMATRIX worldMatrix = gameObjects[i]->m_transform->GetWorldMatrix();
					DirectX::XMMATRIX worldInverseTransposeMatrix = worldMatrix;
					worldInverseTransposeMatrix.r[3] = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 1.0f);
					DirectX::XMVECTOR worldInverseTransposeMatrixDet = DirectX::XMMatrixDeterminant(worldInverseTransposeMatrix);
					worldInverseTransposeMatrix = DirectX::XMMatrixTranspose(DirectX::XMMatrixInverse(&worldInverseTransposeMatrixDet, worldInverseTransposeMatrix));
					DirectX::XMMATRIX textureTransformMatrix = DirectX::XMMatrixIdentity();

					directXClass->GetDeviceContext()->IASetVertexBuffers(0, 1, gameObjects[i]->GetVertexBuffer(), &stride, &offset);
					directXClass->GetDeviceContext()->IASetIndexBuffer(*gameObjects[i]->GetIndexBuffer(), DXGI_FORMAT_R32_UINT, 0);

					directXClass->GetDeviceContext()->Map(m_textureShader->m_vertexShaderBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
					TextureShader::VertexShaderBufferType* vertexShaderBufferPtr = (TextureShader::VertexShaderBufferType*)mappedResource.pData;
					vertexShaderBufferPtr->world = worldMatrix;
					vertexShaderBufferPtr->worldInverseTranspose = worldInverseTransposeMatrix;
					vertexShaderBufferPtr->view = viewMatrix;
					vertexShaderBufferPtr->projection = projectionMatrix;
					vertexShaderBufferPtr->textureTransform = textureTransformMatrix;
					directXClass->GetDeviceContext()->Unmap(m_textureShader->m_vertexShaderBuffer, 0);
					directXClass->GetDeviceContext()->VSSetConstantBuffers(0, 1, &(m_textureShader->m_vertexShaderBuffer));

					directXClass->GetDeviceContext()->Map(m_textureShader->m_pixelShaderBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
					TextureShader::PixelShaderBufferType* pixelShaderBufferPtr = (TextureShader::PixelShaderBufferType*)mappedResource.pData;
					pixelShaderBufferPtr->eyePositionWorld = DirectX::XMFLOAT4(cameraPosition.x, cameraPosition.y, cameraPosition.z, 1.0f);
					pixelShaderBufferPtr->lightsNumber = DirectX::XMINT4(directionalLightsNumber, pointLightsNumber, spotLightsNumber, 0);
					for (int i = 0; (i < directionalLightsNumber) && (i < 100); ++i)
					{
						pixelShaderBufferPtr->directionalLights[i] = directionalLights[i]->GetLightStructure();
					}
					for (int i = 0; (i < pointLightsNumber) && (i < 100); ++i)
					{
						pixelShaderBufferPtr->pointLights[i] = pointLights[i]->GetLightStructure();
					}
					for (int i = 0; (i < spotLightsNumber) && (i < 100); ++i)
					{
						pixelShaderBufferPtr->spotLights[i] = spotLights[i]->GetLightStructure();
					}
					directXClass->GetDeviceContext()->Unmap(m_textureShader->m_pixelShaderBuffer, 0);
					directXClass->GetDeviceContext()->PSSetConstantBuffers(0, 1, &(m_textureShader->m_pixelShaderBuffer));
					directXClass->GetDeviceContext()->PSSetShaderResources(0, 1, &(gameObjects[i]->m_ambientMapShaderResourceView));
					directXClass->GetDeviceContext()->PSSetShaderResources(1, 1, &(gameObjects[i]->m_diffuseMapShaderResourceView));
					directXClass->GetDeviceContext()->PSSetShaderResources(2, 1, &(gameObjects[i]->m_specularMapShaderResourceView));

					directXClass->GetDeviceContext()->DrawIndexed(gameObjects[i]->GetIndexNumber(), 0, 0);
				}
			}
		}
	}
	directXClass->EndScene();
}